package com.xuecheng.system.init;

import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *     扫描指定报下的Class资源
 * </p>
 * 扫描指定报下的关联的Class资源(包括baseClass的实现类和子类信息),其实现是SpringAPI中ResourceLoaderAware获得应用资源信息<br/>
 * 通过Spring提供的 ResourcePatternResolver 和 MetadataReaderFactory 来加载指定位置的资源.<br/>
 * <ul>
 *     <li>ResourcePatternResolver作用:在应用ResourceLoader中加载指定位置资源</li>
 *     <li>MetadataReaderFactory作用:获得得到资源的详细信息</li>
 * </ul>
 *
 *
 * @Description:
 * @Author: Flippy
 */
@Slf4j
@Component
public class SanClassResourceLoader<T> implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }


    /**
     * 根据基础类型Class,扫描指定报下的关联的Class资源(包括baseClass的实现类和子类信息),并返回扫描后的Class集合
     * @param pageName {@link String} 指定要扫描的包名称
     * @param baseClass
     * @return
     */
    public List<Class<T>> loadClassesByPackageName(String pageName,Class<T> baseClass) {

        //1.判断传入信息的合法性
        if (StringUtil.isBlank(pageName)
                || ObjectUtils.isEmpty(baseClass)
            )
            ExceptionCast.cast(CommonErrorCode.E_100101);

        //构建返回结果集合
        List<Class<T>> classList = new ArrayList<>();

        try {

            //2.1将用户指定的报名转为path格式的名称
            String path = ClassUtils.convertClassNameToResourcePath(pageName);

            //2.2构建资源加载处理器
            ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);

            //2.4获得指定包下的资源集合信息
            Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+path +"/**/*.class");

            //2.5构建获得资源详情读取器工程
            MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);

            //2.6获得类加载器,为获得Class使用
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

            //2.7 循环遍历获得的指定包下的资源,并跟baseClass进行判断,最终获得Class信息
            for (Resource resource : resources) {

                //1.加载单个资源进行解析器中的详细信息,并获得资源详情读取器
                MetadataReader reader = metaReader.getMetadataReader(resource);

                //2.根据详情中的包名获得资源的Class对象
                Class<?> aClass = ClassUtils.forName(reader.getClassMetadata().getClassName(), contextClassLoader);

                //3.判断Class对象是否是baseClass的实现类或子类
                boolean assignable = ClassUtils.isAssignable(baseClass, aClass);

                //4.如果是,则添加到结果集合中
                if (assignable) {
                    classList.add((Class<T>) aClass);
                }
            }

        } catch (Exception e) {

            //如果加载失败记录日志中
            log.error("初始化常量值失败",e);
        }

        //3.返回结果信息
        return classList;
    }




}